package model;

import game.util.FileUtil;

import java.util.HashMap;

import org.lwjgl.util.vector.Vector3f;

/**
 * 
 * @author Jevgeni Krutov
 * @author Sri Harsha Chilakapati
 *
 */
public class ModelFactory {
	/**
	 * Mesh storage
	 */
	private static HashMap<String, Model> modelStorage = new HashMap<String, Model>();

	public static Model getModelInstance(String path) {
		if(modelStorage.containsKey(path)) {
			return modelStorage.get(path);
		} else {
			storeModel(path);
			return getModelInstance(path);
		}
	}	
	
	private static void storeModel(String pathToObjFile) {
		// Create an empty model and a null material
		Model blankModel = new Model();
		Material material = null;

		// Read all the lines from the source file
		String[] lines = FileUtil.readAllLines(pathToObjFile);

		// Iterate and parse the lines
		for (String line : lines) {

			// If the line starts with "v" then it is a vertex
			
			//VARIANT 1: 3dsmax/gmax obj
			if (line.startsWith("v  ")) {
				// Split the line
				String[] values = line.split(" ");

				// Parse the coordinates
				float x = Float.parseFloat(values[2]);
				float y = Float.parseFloat(values[3]);
				float z = Float.parseFloat(values[4]);

				// Create the vertex
				Vector3f vertex = new Vector3f(x, y, z);
				blankModel.vertices.add(vertex);
			}
			
//			//VARIANT 2 : blender obj
			else if (line.startsWith("v ")) {
				// Split the line
				String[] values = line.split(" ");

				// Parse the coordinates
				float x = Float.parseFloat(values[1]);
				float y = Float.parseFloat(values[2]);
				float z = Float.parseFloat(values[3]);

				// Create the vertex
				Vector3f vertex = new Vector3f(x, y, z);
				blankModel.vertices.add(vertex);
			}
			// If the line starts with "vn" then it is a vertex normal
			
			//3dsmax/gmax obj
			else if (line.startsWith("vn  ")) {
				// Split the line
				String[] values = line.split(" ");

				// Parse the values
				float x = Float.parseFloat(values[2]);
				float y = Float.parseFloat(values[3]);
				float z = Float.parseFloat(values[4]);

				// Create the normal
				Vector3f normal = new Vector3f(x, y, z);
				blankModel.normals.add(normal);
			}
			
			//blender obj
			else if (line.startsWith("vn ")) {
				// Split the line
				String[] values = line.split(" ");

				// Parse the values
				float x = Float.parseFloat(values[1]);
				float y = Float.parseFloat(values[2]);
				float z = Float.parseFloat(values[3]);

				// Create the normal
				Vector3f normal = new Vector3f(x, y, z);
				blankModel.normals.add(normal);
			}
			// If the line starts with "f" then it is a face
			else if (line.startsWith("f ")) {
				// Split the line
				String[] values = line.split(" ");

				// Parse the vertex indices
				float v1 = Float.parseFloat(values[1].split("/")[0]);
				float v2 = Float.parseFloat(values[2].split("/")[0]);
				float v3 = Float.parseFloat(values[3].split("/")[0]);

				// Create the vertex
				Vector3f vertex = new Vector3f(v1, v2, v3);

				// Parse the normal indices
				float vn1 = Float.parseFloat(values[1].split("/")[2]);
				float vn2 = Float.parseFloat(values[2].split("/")[2]);
				float vn3 = Float.parseFloat(values[3].split("/")[2]);

				// Create the normal
				Vector3f normal = new Vector3f(vn1, vn2, vn3);

				// Create the face
				blankModel.faces.add(new Face(vertex, normal, material));
			}
			// If the line starts with "mtllib" then it specifies the file
			// that contains the material definitions.
			else if (line.startsWith("mtllib ")) {
				// Parse the material file
				parseMaterial(blankModel, FileUtil.getFileInSameLevelOf(pathToObjFile, line.replaceAll("mtllib ", "").trim()));
			}
			// The command "usemtl" states the name of material to
			// use for upcoming faces
			else if (line.startsWith("usemtl ")) {
				// Get the material
				material = blankModel.materials.get(line.replaceAll("usemtl ", "").trim());
			}
		}
		// Prepare the VBO for the model
		blankModel.prepareVBO();

		blankModel.calculateBB();
		
		modelStorage.put(pathToObjFile, blankModel);
	}
	
	/**
	 * Parses the material library file
	 * @author Sri Harsha Chilakapati
	 */
	private static void parseMaterial(Model bm, String name) {
		// The material to be created
		Material material = null;

		// Read all the lines from the library
		String[] lines = FileUtil.readAllLines(name);

		// Parse the library by iterating over all lines
		for (String line : lines) {
			// "newmtl" defines a new material
			if (line.startsWith("newmtl ")) {
				if (material != null) {
					// Add the previous material to the list
					bm.materials.put(material.getName(), material);
				}

				// Create a new material
				material = new Material(line.split(" ", 2)[1]);
			}
			// "Kd" defines the diffuse color of the material
			else if (line.startsWith("Kd ")) {
				// Split the line
				String[] values = line.split(" ");

				// Parse the values
				float r = Float.parseFloat(values[1]);
				float g = Float.parseFloat(values[2]);
				float b = Float.parseFloat(values[3]);

				// Create the color
				Vector3f color = new Vector3f(r, g, b);

				// Set the color of the material
				material.setDiffuse(color);
			}
		}

		// Add the material to the list
		bm.materials.put(material.getName(), material);
	}
}
